package xyz.riceball.job.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Service;
import xyz.riceball.framework.core.context.UserContext;
import xyz.riceball.framework.core.enums.ENBool;
import xyz.riceball.framework.core.exception.RiceBallBizErrorException;
import xyz.riceball.framework.core.exception.RiceBallTaskException;
import xyz.riceball.framework.core.util.BeanUtils;
import xyz.riceball.framework.core.util.JobUtils;
import xyz.riceball.job.entity.dto.JobTriggerDTO;
import xyz.riceball.job.entity.po.JobTriggerPO;
import xyz.riceball.job.listener.RbSchedulerListener;
import xyz.riceball.job.mapper.JobTriggerMapper;
import xyz.riceball.job.service.JobLogService;
import xyz.riceball.job.service.JobService;

import javax.annotation.Resource;
import java.time.LocalDateTime;

/**
 * <p>
 * 任务业务接口实现类
 * </p>
 *
 * @author xiaovcloud
 * @since 2022/6/12 21:14
 */
@Service
@Slf4j
public class JobServiceImpl extends ServiceImpl<JobTriggerMapper, JobTriggerPO> implements JobService {

    @Resource
    private Scheduler scheduler;
    @Resource
    private JobLogService jobLogService;
    @Resource
    private JobTriggerMapper jobTriggerMapper;

    @Override
    public JobTriggerPO addJob(JobTriggerDTO jobTriggerDto) {
        LocalDateTime startTime = LocalDateTime.now();
        checkBeforeDb(jobTriggerDto);
        QuartzJobBean quartzJobBean = JobUtils.getClass(jobTriggerDto.getExecutorHandler());
        //执行参数
        String executorParams = jobTriggerDto.getExecutorParams();

        JobDataMap jobDataMap = JSONUtil.toBean(executorParams, JobDataMap.class);
        JobDetail jobDetail = JobBuilder.newJob(quartzJobBean.getClass())
                .withIdentity(jobTriggerDto.getJobName())
                .usingJobData(jobDataMap).build();
        // Cron表达式调度构建器(即任务执行的时间)
        CronScheduleBuilder cron = CronScheduleBuilder.cronSchedule(jobTriggerDto.getCronExpression());

        //根据Cron表达式构建一个Trigger
        CronTrigger trigger = TriggerBuilder.newTrigger()
                .withIdentity(jobTriggerDto.getJobName())
                .startNow()
                .withSchedule(cron).build();

        JobTriggerPO jobTriggerPO = new JobTriggerPO();
        BeanUtils.copyWithoutNull(jobTriggerPO, jobTriggerDto);
        jobTriggerPO.setStatus(ENBool.TRUE.getValue());
        BeanUtils.buildAutoField(jobTriggerPO, true, UserContext.getUserId());
        int count = getBaseMapper().insert(jobTriggerPO);
        if (count == 1) {
            log.info("新增任务，修改数据库成功");
            //TODO 事物操作
            try {
                scheduler.getListenerManager().addSchedulerListener(new RbSchedulerListener());
                scheduler.scheduleJob(jobDetail, trigger);
                scheduler.start();
                jobLogService.logSuccess(jobTriggerPO, startTime, LocalDateTime.now(), "新增任务，调度成功");
            } catch (SchedulerException e) {
                log.error("【定时任务】创建失败！", e);
                jobLogService.logError(jobTriggerPO, startTime, LocalDateTime.now(), "新增任务，调度失败", e.getMessage());
                throw new RiceBallTaskException("【定时任务】创建失败！");
            }
        } else {
            log.error("新增任务，修改数据库失败");
            throw new RiceBallTaskException("新增任务，修改数据库失败");
        }
        return jobTriggerPO;
    }

    /**
     * 数据库操作前的检测
     *
     * @param jobTriggerDto
     */
    private void checkBeforeDb(JobTriggerDTO jobTriggerDto) {
        if (StrUtil.isEmpty(jobTriggerDto.getId())) {
            LambdaQueryWrapper<JobTriggerPO> wrapper = Wrappers.<JobTriggerPO>lambdaQuery();
            wrapper.eq(JobTriggerPO::getJobName, jobTriggerDto.getJobName());
            JobTriggerPO jobTriggerPO = jobTriggerMapper.selectOne(wrapper);
            if (jobTriggerPO != null) {
                throw new RiceBallBizErrorException("任务名不能重复");
            }
        }
    }

    @Override
    public void deleteJob(String id) {
        JobTriggerPO jobTriggerPO = getBaseMapper().selectById(id);
        if (jobTriggerPO == null) {
            log.error("未找到id为：[{}]的任务", id);
            throw new RiceBallBizErrorException("未找到任务");
        }
        TriggerKey triggerKey = TriggerKey.triggerKey(jobTriggerPO.getJobName());
        JobKey jobKey = JobKey.jobKey(jobTriggerPO.getJobName());
        try {
            int count = getBaseMapper().deleteById(id);
            if (count == 1) {
                log.info("删除任务，修改数据库成功");
            } else {
                log.error("删除任务，修改数据库失败,id: {}", id);
            }
            scheduler.pauseTrigger(triggerKey);
            scheduler.unscheduleJob(triggerKey);
            scheduler.deleteJob(jobKey);
        } catch (SchedulerException e) {
            log.error("scheduler删除任务失败", e);
            throw new RiceBallTaskException("删除任务失败");
        }
    }

    @Override
    public void pauseJob(String id) {
        JobTriggerPO jobTriggerPO = getBaseMapper().selectById(id);
        if (jobTriggerPO == null) {
            log.error("未找到id为：[{}]的任务", id);
            throw new RiceBallTaskException("未找到任务");
        }
        jobTriggerPO.setStatus(ENBool.FALSE.getValue());
        BeanUtils.buildAutoField(jobTriggerPO, false, UserContext.getUserId());
        int count = getBaseMapper().updateById(jobTriggerPO);
        if (count == 1) {
            log.info("停止任务，修改数据库成功");
            try {
                scheduler.pauseJob(JobKey.jobKey(jobTriggerPO.getJobName()));
            } catch (SchedulerException e) {
                throw new RiceBallTaskException(e);
            }
        } else {
            log.error("停止任务，修改数据库失败");
        }

    }

    @Override
    public void resumeJob(String id) {
        LocalDateTime startTime = LocalDateTime.now();
        JobTriggerPO jobTriggerPO = getBaseMapper().selectById(id);
        if (jobTriggerPO == null) {
            log.error("未找到id为：[{}]的任务", id);
            throw new RiceBallBizErrorException("未找到任务");
        }
        jobTriggerPO.setStatus(ENBool.TRUE.getValue());
        BeanUtils.buildAutoField(jobTriggerPO, false, UserContext.getUserId());
        int count = getBaseMapper().updateById(jobTriggerPO);
        if (count == 1) {
            log.info("重启任务，修改数据库成功");
            try {
                scheduler.resumeJob(JobKey.jobKey(jobTriggerPO.getJobName()));
                jobLogService.logSuccess(jobTriggerPO, startTime, LocalDateTime.now(), "重启任务，调度成功");
            } catch (SchedulerException e) {
                jobLogService.logError(jobTriggerPO, startTime, LocalDateTime.now(), "重启任务，调度失败", e.getMessage());
                throw new RiceBallTaskException(e);
            }
        } else {
            log.error("重启任务，修改数据库失败,id:{}", id);
        }
    }

    /**
     * 修改任务
     *
     * @param jobTriggerDto
     */
    @Override
    public void cornJob(JobTriggerDTO jobTriggerDto) {
        LocalDateTime startTime = LocalDateTime.now();
        JobTriggerPO jobTriggerPO = getBaseMapper().selectById(jobTriggerDto.getId());
        if (jobTriggerPO == null) {
            log.error("未找到id为：[{}]的任务", jobTriggerDto.getId());
            throw new RiceBallBizErrorException("未找到任务");
        }
        TriggerKey triggerKey = TriggerKey.triggerKey(jobTriggerPO.getJobName());
        jobTriggerPO.setCronExpression(jobTriggerPO.getCronExpression());
        BeanUtils.buildAutoField(jobTriggerPO, false, UserContext.getUserId());

        // 表达式调度构建器
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(jobTriggerDto.getCronExpression());

        int count = getBaseMapper().updateById(jobTriggerPO);
        if (count == 1) {
            log.info("修改任务corn表达式，修改数据库成功");
            if (ENBool.TRUE.getValue().equals(jobTriggerPO.getStatus())) {
                //只有在运行中的任务才会重新调度
                try {
                    CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);

                    // 根据Cron表达式构建一个Trigger
                    trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).startNow().build();
                    jobLogService.logSuccess(jobTriggerPO, startTime, LocalDateTime.now(), "修改任务后，重新调度成功");
                    // 按新的trigger重新设置job执行
                    scheduler.rescheduleJob(triggerKey, trigger);
                } catch (SchedulerException e) {
                    jobLogService.logError(jobTriggerPO, startTime, LocalDateTime.now(), "修改任务后，重新调度失败", e.getMessage());
                    throw new RiceBallTaskException(e);
                }
            }
        } else {
            log.error("修改任务corn表达式，修改数据库失败。任务id：{}", jobTriggerDto.getId());
            throw new RiceBallTaskException("修改任务失败");
        }

    }

    @Override
    public Page<JobTriggerPO> selectPage(Page<JobTriggerPO> page, JobTriggerPO jobTriggerPO) {
        LambdaQueryWrapper<JobTriggerPO> wrapper = Wrappers.<JobTriggerPO>lambdaQuery();
        if (StrUtil.isNotEmpty(jobTriggerPO.getJobName())) {
            wrapper.eq(JobTriggerPO::getJobName, jobTriggerPO.getJobName());
        }
        if (StrUtil.isNotEmpty(jobTriggerPO.getExecutorHandler())) {
            wrapper.eq(JobTriggerPO::getExecutorHandler, jobTriggerPO.getExecutorHandler());
        }
        if (StrUtil.isNotEmpty(jobTriggerPO.getCronExpression())) {
            wrapper.eq(JobTriggerPO::getCronExpression, jobTriggerPO.getCronExpression());
        }
        if (StrUtil.isNotEmpty(jobTriggerPO.getStatus())) {
            wrapper.eq(JobTriggerPO::getStatus, jobTriggerPO.getStatus());
        }
        return getBaseMapper().selectPage(page, wrapper);
    }
}
